import { renderData, rebuild } from './render'

/**
 * 代理数据
 * @param {*} vm 
 * @param {*} data 
 * @param {*} namespace 主要控制当前data是哪一个 name 还是 object.name 访问跟下面的属性就是 '' 
 */
export function constructProxy(vm, data, namespace) {
   let proxyObj = null;
   // 我们需要判断data的类型
   if(data instanceof Array) { // 需要先判断Array类型
      proxyObj = proxyArray(vm, data, namespace)
   } else if (data instanceof Object) { // 判断Object类型
      proxyObj = proxyObject(vm, data, namespace)
   } else {
      throw new Error('data must be an object or array')
   }
   return proxyObj
}

/**
 * 代理Object数据
 * @param {*} vm 
 * @param {*} data 
 */
function proxyObject(vm, data, namespace) {
    // 创建代理对象
    let proxyObj = {}
    for(let key in data) {
        Object.defineProperty(proxyObj, key, {
            configurable: true, // 可配置
            get() {
                return data[key]
            },
            set(newValue) {
                data[key] = newValue
                renderData(vm, getNameSpace(namespace, key))
            }
        })
        // 为MiniVue的根实例添加属性
        Object.defineProperty(vm, key, {
            configurable: true, // 可配置
            get() {
                return data[key]
            },
            set(newValue) {
                data[key] = newValue
                renderData(vm, getNameSpace(namespace, key))
            }
        })
        if(typeof data[key] === 'object') {
            proxyObj[key] = constructProxy(vm, data[key], getNameSpace(namespace, key))
        }
    }
    return proxyObj
}

/**
 * 代理Array数据
 * @param {*} vm 
 * @param {*} data 
 */
function proxyArray(vm, array, namespace) {
   // 创建代理对象
   let proxyObj = new Array(array.length)
   // 给数组的每一个元素都创建代理
   array.forEach((item, index) => {
        proxyObj[index] = constructProxy(vm, item, namespace)
   })
   // 代理数组的方法
   proxyObj = defineArrayProto(array, namespace, vm)
   return proxyObj
}


/**
 * 代理Array方法
 * @param {*} array 
 * @param {*} namespace 
 * @returns 
 */
function defineArrayProto(array = [], namespace, vm) {
    // 创建代理对象
    const obj = {
        type: 'Array',
        toString() {
            let result = "";
            for (let i = 0 ; i < array.length ; i ++) {
                result += array[i] + ", "
            }
            return result.substring(0, result.length - 2);
        },
        push() {},
        pop() {},
        shift() {},
        unshift() {}
    }
    // 代理对象中的方法
    defineProxyMethod.call(vm, obj, 'push', namespace)
    defineProxyMethod.call(vm, obj, 'pop', namespace)
    defineProxyMethod.call(vm, obj, 'shift', namespace)
    defineProxyMethod.call(vm, obj, 'unshift', namespace)
    // 将原数组的隐式原型设置为代理对象
    array.__proto__ = obj
    return array
}

// 获取Array的原型
const arrayProto = Array.prototype

/**
 * 代理执行数组方法
 * @param {*} obj 
 * @param {*} func 
 * @param {*} namespace 
 */
function defineProxyMethod(obj, func, namespace) {
    const vm = this
    Object.defineProperty(obj, func, {
        configurable: true, // 可配置
        enumerable: true, // 可枚举
        value: function() {
            // 获取执行方法
            let method = arrayProto[func]
            // 执行数组方法获取返回结果
            let result = method.call(this, ...arguments) 
            rebuild(vm, getNameSpace(namespace, ''))
            return result; // 返回执行结果
        }
    })
}

function getNameSpace(nowNameSpace, nowProp) {
    if (nowNameSpace == null || nowNameSpace == "") {
        return nowProp;
    } else if (nowProp == null || nowProp == "") {
        return nowNameSpace;
    } else {
        return nowNameSpace + "." + nowProp;
    }
}